using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace DareMusicFixer
{
    #region Data
    struct DareMp3TagData
    {
        public DareMp3TagData(DareMp3TagData dtag)
            : this(dtag.mFilename, dtag.mAlbum, dtag.mArtist, dtag.mTitle, dtag.mTrack, dtag.mImage)
        {
        }

        public DareMp3TagData(string filename, string album, string artist, string title, uint track, byte[] image)
        {
            mFilename = filename;
            mAlbum = album;
            mArtist = artist;
            mTitle = title;
            mTrack = track;
            mImage = image;
        }

        public override bool Equals(object obj)
        {
            bool toReturn = false;
            DareMp3TagData dtag = (DareMp3TagData)obj;
            
            //Log.WriteLine("Testing Equality");

            if (mFilename == dtag.mFilename &&
                mAlbum == dtag.mAlbum &&
                mArtist == dtag.mArtist &&
                mTitle == dtag.mTitle &&
                mTrack == dtag.mTrack
                )
            {
                //Log.WriteLine("Testing Image");
                if (mImage == null || dtag.mImage == null)
                {
                    if (mImage == dtag.mImage)
                    {
                        //Log.WriteLine("Images both null");
                        toReturn = true;
                    }
                    else
                    {
                        //Log.WriteLine("one image null");
                        toReturn = false;
                    }
                }
                else if (mImage.Length == dtag.mImage.Length)
                {
                    //Log.WriteLine("Images both same length.");
                    toReturn = true;
                    for (int i = 0; i < mImage.Length; i++)
                    {
                        if (mImage[i] != dtag.mImage[i])
                        {
                            //Log.WriteLine("Different Data.");
                            toReturn = false;
                        }
                    }
                }
                else
                {
                    //Log.WriteLine("Else");
                    toReturn = false;
                }
            }

            return toReturn;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public string mFilename;
        public string mAlbum;
        public string mArtist;
        public string mTitle;
        public uint mTrack;
        public byte[] mImage;
    }
    #endregion

    public class DareMp3Tag
    {
        #region Private Variables
        DareMp3TagData mOriginalData;
        DareMp3TagData mCurrentData;

        bool mForceWrite;
        TagLib.File mTagLibFile;
        #endregion

        #region Constructor
        public DareMp3Tag(string filename)
        {
            if (System.IO.File.Exists(filename))
            {
                LoadFromTagLibFile(filename);
            }            
        }
        #endregion

        #region Public Properties
        public string Filename
        {
            get { return mCurrentData.mFilename; }
            set
            {
                mCurrentData.mFilename = value;
                try
                {
                    mTagLibFile = TagLib.File.Create(value);
                }
                catch (Exception e)
                {
                    Log.WriteLine(e.Message);
                }
            }
        }

        public string Album
        {
            get { return mCurrentData.mAlbum; }
            set { mCurrentData.mAlbum = value; }
        }

        public string Artist
        {
            get { return mCurrentData.mArtist; }
            set { mCurrentData.mArtist = value; }
        }

        public string Title
        {
            get { return mCurrentData.mTitle; }
            set { mCurrentData.mTitle = value; }
        }

        public uint Track
        {
            get { return mCurrentData.mTrack; }
            set { mCurrentData.mTrack = value; }
        }

        public byte[] Image
        {
            get { return mCurrentData.mImage; }
            set { mCurrentData.mImage = value; }
        }

        public bool ImageIsValid
        {
            get { return (Image != null && Image.Length != 0); }
        }

        public bool IsValid
        {
            get { return (Title != "" && Track != 0 && Album != "" && Artist != "" && ImageIsValid); }
        }

        public bool ForceWrite
        {
            get { return mForceWrite; }
            set { mForceWrite = value; }
        }

        public TagLib.File TagLibFile
        {
            get { return mTagLibFile; }
        }
        #endregion

        #region Public Helpers
        public void Save()
        {
            try
            {
                if (NeedToWrite())
                {
                    Log.WriteLine("Saving Tags");

                    RemoveAllTags();

                    mTagLibFile = TagLib.File.Create(Filename);

                    mTagLibFile.Tag.Album = Album;
                    mTagLibFile.Tag.Performers = new string[] { Artist };
                    mTagLibFile.Tag.Title = Title;
                    mTagLibFile.Tag.Track = Track;
                    if (ImageIsValid)
                    {
                        mTagLibFile.Tag.Pictures = new TagLib.IPicture[] { new TagLib.Picture(new TagLib.ByteVector(Image)) };
                    }
                    mTagLibFile.Save();
                    Log.WriteLine("Done");
                }
                else
                {
                    Log.WriteLine("No changes made, tag doesn't need to be saved.");
                }
            }
            catch (Exception e)
            {
                Log.WriteLine(e.Message);
            }
        }

        public void RemoveAllTags()
        {
            try
            {
                byte[] buffer = new byte[4096];
                using (FileStream fs = new FileStream(Filename + ".bin", FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    using (FileStream s = new FileStream(Filename, FileMode.Open, FileAccess.Read))
                    {
                        s.Seek(mTagLibFile.InvariantStartPosition, SeekOrigin.Begin);
                        int count = 0;

                        do
                        {
                            int lengthToRead = buffer.Length;

                            if (s.Position + buffer.Length > mTagLibFile.InvariantEndPosition)
                            {
                                lengthToRead = (int)(mTagLibFile.InvariantEndPosition - s.Position);
                            }

                            count = s.Read(buffer, 0, lengthToRead);
                            fs.Write(buffer, 0, count);
                        } while (count != 0 && s.Position <= mTagLibFile.InvariantEndPosition);
                    }
                }

                // delete original
                File.Delete(Filename);

                // rename to original
                File.Move(Filename + ".bin", Filename);
            }
            catch (Exception e)
            {
                Log.WriteLine(e.Message);
            }
        }
        #endregion

        #region Private Helpers
        private bool NeedToWrite()        
        {
            return !mOriginalData.Equals(mCurrentData) || ForceWrite;
        }

        private void LoadFromTagLibFile(string file)
        {            
            Log.WriteBreak();

            Filename = file;
            Log.WriteLine("File: " + Filename);

            if (mTagLibFile != null)
            {

                Artist = "";
                Artist = mTagLibFile.Tag.JoinedPerformers;
                if (Artist == "")
                {
                    Artist = mTagLibFile.Tag.JoinedAlbumArtists;
                }
                Log.WriteLine("Artist: " + Artist);

                Album = mTagLibFile.Tag.Album;
                Log.WriteLine("Album: " + Album);

                Title = mTagLibFile.Tag.Title;
                Log.WriteLine("Title: " + Title);

                Track = mTagLibFile.Tag.Track;
                Log.WriteLine("Track: " + Track);

                if (mTagLibFile.Tag.Pictures.Length > 0)
                {
                    Image = mTagLibFile.Tag.Pictures[0].Data.Data;

                    Log.WriteLine("Image Size: " + ImageHelpers.ImageSizeKB(Image.Length, 2) + "KB");

                    System.Drawing.Image temp = ImageHelpers.ByteArrayToImage(Image);
                    Log.WriteLine("Width: " + temp.Width + " Height: " + temp.Height);
                    temp.Dispose();
                }
                else
                {
                    Image = null;
                }

                mOriginalData = new DareMp3TagData(mCurrentData);
            }
            else
            {
                Log.WriteLine("Problem with mp3.");
            }
            
            Log.WriteBreak();
        }
        #endregion
    }
}
